package cn.edu.lingnan.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
import java.util.Arrays;

@Configuration  //表示当前类属于注释类
@EnableCaching  //表示支持缓存
/**
 * 配置Spring Cache 的 CacheManger 需要继承 CachingConfigurerSupport类，重写CacheManager方法
 * 在此之前，确保spring容器里面有redisTemplate, 需要redisTemplate 获取 LettuceConnectionFactory
 */
public class RedisConfig extends CachingConfigurerSupport {
    /**
     * 默认情况下 redisTemplate 模板使用的是 jdk 方式进行序列化操作，不是很方便，所有需要重写的序列化的实现
     * @return
     */

    /**
     自定义key生成策略
     策略：包名+类名+方法名+参数
     */
    @Bean
    public KeyGenerator keyGenerator(){
        return ((target,method,params) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getName()).append(".");
            sb.append(method.getName()).append(Arrays.toString(params));
            return sb.toString();
        });
    }


    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){

        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        //设置序列化操作
        Jackson2JsonRedisSerializer<Object> Jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.PROPERTY);

        Jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        RedisSerializer<?> stringSerializer = new StringRedisSerializer();

        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setValueSerializer(Jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);
        redisTemplate.setHashValueSerializer(Jackson2JsonRedisSerializer);


        return redisTemplate;
    }

    /**
     * CacheManager是对Cache进行管理、创建、获取、销毁等操作的，在创建Cache时，需要对其进行序列化
     *  在重写cacheManager时，通过RedisCacheConfiguration 对缓存数据的key、value进行序列化操作。
     *  key对应String格式
     *  value对应JSON格式，其目的也是为了解决中文乱码问题
     */

    @Bean
    public CacheManager cacheManager(RedisTemplate redisTemplate){
        //spring cache 注解序列化配置，分别创建 String 和 Json 格式序列化对象，对缓存器key和value进行转换

        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
        //解决中文乱码问题
        //key序列化操作
        redisCacheConfiguration.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getKeySerializer()));
        //配置缓存管理器json序列化器 //value序列化方式
        redisCacheConfiguration.serializeValuesWith(
                RedisSerializationContext.SerializationPair
                        .fromSerializer(redisTemplate.getValueSerializer()))    //不缓存null值
                .disableCachingNullValues().entryTtl(Duration.ofMillis(60));    //默认缓存过期时间
        //使用自定义的配置构建缓存管理器
        RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisTemplate.getConnectionFactory())
                                                .cacheDefaults(redisCacheConfiguration).build();

        return redisCacheManager;
    }


}
